/**
 * MotionTrajectory 类用于在 Cesium 查看器中创建和管理运动轨迹。
 * 该类封装了轨迹数据处理、实体添加和动画控制等功能。
 */
class MotionTrajectory {
  /**
   * 构造函数，初始化 MotionTrajectory 实例。
   *
   * @param {Cesium.Viewer} viewer - Cesium 查看器实例，用于显示地理数据。
   * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
   * @param {string} modelUri - 模型的 URI，用于在轨迹上显示的模型。
   */
  constructor(viewer, data, modelUri) {
    // 存储传入的 Cesium 查看器实例
    this.viewer = viewer;
    // 存储轨迹数据
    this.data = this.createByGeojson(data);
    // 存储模型的 URI
    this.modelUri = modelUri;
    // 创建一个自定义数据源，用于存储轨迹相关的实体
    this.datasoure = new Cesium.CustomDataSource('entiestestdata');
    // 将自定义数据源添加到查看器中
    this.viewer.dataSources.add(this.datasoure);
    // 初始化轨迹
    // this.initLocus();
  }

  /**
   * 初始化轨迹，包括生成路径数据、添加路径到数据源、创建位置属性和添加带有模型和动画的实体。
   */
  initLocus() {
    // 生成路径数据
    const pathData = this.generatePathData(this.data);
    // 将路径添加到数据源中
    this.addPathToDatasource(this.datasoure, pathData);
    // 创建位置属性，用于控制模型的位置
    const property = this.createPositionProperty(this.data);
    // 获取停止时间，用于动画的结束时间
    const dates = this.getStopTime(this.data);

    // 添加带有模型和动画的实体到数据源
    this.addEntityWithModelAndAnimation(property, dates, this.datasoure);

    // this.followModel(property);
  }

  /**
   * 从轨迹数据中生成路径数据。
   *
   * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
   * @returns {Array} - 路径数据数组，包含所有经度、纬度和高度值。
   */
  generatePathData(data) {
    let pathData = [];
    // 遍历轨迹数据，将每个元素的经度、纬度和高度添加到路径数据数组中
    data.forEach((item) => {
      pathData.push(item[0], item[1], item[2]);
    });
    return pathData;
  }

  /**
   * 将路径添加到指定的数据源中。
   *
   * @param {Cesium.CustomDataSource} datasoure - 要添加路径的数据源。
   * @param {Array} pathData - 路径数据数组，包含所有经度、纬度和高度值。
   */
  addPathToDatasource(datasoure, pathData) {
    // 在数据源中添加一个实体，用于显示路径
    datasoure.entities.add({
      name: "line",
      polyline: {
        // 将路径数据转换为笛卡尔坐标数组
        positions: Cesium.Cartesian3.fromDegreesArrayHeights(pathData),
        // 路径的颜色
        material: Cesium.Color.YELLOW,
        // 路径的宽度
        width: 1,
        // 高度参考类型
        heightReference: Cesium.HeightReference.NONE,
        // 禁用深度测试的距离
        disableDepthTestDistance: Number.POSITIVE_INFINITY,
      },
    });
  }

  /**
   * 创建一个采样位置属性，用于控制模型的位置，并在每个采样点添加一个点实体。
   *
   * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
   * @returns {Cesium.SampledPositionProperty} - 采样位置属性实例。
   */
  createPositionProperty(data) {
    // 创建一个采样位置属性实例
    let property = new Cesium.SampledPositionProperty();
    // 遍历轨迹数据，为每个点添加采样信息
    data.forEach((item, index) => {
      // 计算当前点的时间
      let time = new Date(Date.now() + index * 70000);
      // 将当前点的经纬度和高度转换为笛卡尔坐标
      let position = Cesium.Cartesian3.fromDegrees(item[0], item[1], item[2]);
      // 为采样位置属性添加一个采样点
      property.addSample(Cesium.JulianDate.fromDate(time), position);
      // 为点实体添加一个特殊标识，方便后续清除
      this.viewer.entities.add({
        position: position,
        point: {
          // 点的像素大小
          pixelSize: 10,
          // 点的颜色
          color: Cesium.Color.RED,
          // 点的轮廓宽度
          outlineWidth: 2,
          // 点的轮廓颜色
          outlineColor: Cesium.Color.DARKRED,
        },
        isMotionTrajectoryPoint: true // 添加标识
      });
    });
    // 设置采样位置属性的插值选项
    property.setInterpolationOptions({
      interpolationDegree: 0.01,
      interpolationAlgorithm: Cesium.LagrangePolynomialApproximation,
    });
    return property;
  }

  /**
   * 根据轨迹数据计算停止时间。
   *
   * @param {Array} data - 轨迹数据数组，每个元素是一个包含经度、纬度和高度的数组。
   * @returns {Date} - 停止时间的日期对象。
   */
  getStopTime(data) {
    // 计算停止时间，为当前时间加上轨迹数据长度乘以 10000 毫秒
    const stoptime = new Date(Date.now() + (data.length - 1) * 70000);
    return stoptime;
  }

  /**
   * 添加一个带有模型和动画的实体到指定的数据源中，并设置时钟和动画选项。
   *
   * @param {Cesium.SampledPositionProperty} property - 采样位置属性，用于控制模型的位置。
   * @param {Date} dates - 停止时间的日期对象，用于动画的结束时间。
   * @param {Cesium.CustomDataSource} datasource - 要添加实体的数据源。
   */
  addEntityWithModelAndAnimation(property, dates, datasource) {
    // 在查看器中添加一个带有模型和动画的实体
    this.viewer.entities.add({
      // 设置实体的可用时间区间
      availability: new Cesium.TimeIntervalCollection([
        new Cesium.TimeInterval({
          start: Cesium.JulianDate.fromDate(new Date()),
          stop: Cesium.JulianDate.fromDate(dates),
        }),
      ]),
      // 设置实体的 z 索引
      zIndex: 1,
      // 设置实体的位置属性
      position: property,
      // 设置实体的方向属性，根据速度计算方向
      orientation: new Cesium.VelocityOrientationProperty(property),
      model: {
        // 模型的 URI
        uri: this.modelUri,
        // 模型的缩放比例
        scale: 1.0,
      },
      path: {
        // 路径的分辨率
        resolution: 1,
        // 路径的材质
        material: new Cesium.PolylineGlowMaterialProperty({
          glowPower: 0.1,
          color: Cesium.Color.YELLOW
        }),
        // 路径的宽度
        width: 10
      }
    });
    // 设置查看器时钟的当前时间
    this.viewer.clock.currentTime = Cesium.JulianDate.fromDate(new Date());
    // 设置查看器时钟的停止时间
    this.viewer.clock.stopTime = Cesium.JulianDate.fromDate(dates);
    // 设置查看器时钟的范围为固定
    this.viewer.clock.clockRange = Cesium.ClockRange.CLAMPED;
    // 让查看器飞到指定的数据源
    // this.viewer.flyTo(datasource);
    // 启用查看器时钟的动画功能
    this.viewer.clock.shouldAnimate = true;
  }

  /**
   * 清除轨迹相关的数据源和实体。
   */
  clear() {
    // 从查看器中移除自定义数据源
    this.viewer.dataSources.remove(this.datasoure, true);
    // 存储需要移除的实体
    const entitiesToRemove = [];
    // 遍历查看器中的所有实体，找出需要移除的实体
    this.viewer.entities.values.forEach((entity) => {
      if (entity.name === "line" || entity.model || entity.isMotionTrajectoryPoint) {
        entitiesToRemove.push(entity);
      }
    });
    // 移除所有需要移除的实体
    entitiesToRemove.forEach((entity) => {
      this.viewer.entities.remove(entity);
    });
  }



  /**
   * 由geojson格式数据
   * @param {Object} data geojson格式数据
  */
  createByGeojson(data) {
    let { features } = data;
    let dataarr = []
    //  
    for (let i = 0; i < features.length; i++) {
      let feature = features[i];
      const { properties, geometry } = feature;
      geometry.coordinates.forEach((e) => {
        dataarr.push([e[0], e[1], e[2]])
      })
    }
    return dataarr
  }

  /**
   * 设置视角跟随模型
   * @param {Cesium.SampledPositionProperty} property - 采样位置属性，用于控制模型的位置。
   */
  followModel(property) {
    const viewer = this.viewer;
    viewer.trackedEntity = viewer.entities.add({
      position: property,
      point: {
        pixelSize: 0, // 不显示点
        color: Cesium.Color.TRANSPARENT
      }
    });
  }

}

export default MotionTrajectory;